package com.merry.event;

import java.util.concurrent.Executor;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.AbstractApplicationEventMulticaster;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.util.ErrorHandler;

/**
 * 一个简单的实现类。
 * 广播所有的事件向已经注册了的监听器，
 * 将所有事件多播给所有已注册的侦听器，让侦听器忽略他们不感兴趣的事件。
 * 侦听器通常会对传入的事件对象执行相应的{@code instanceof}检查。也就是 {ApplicationEvent的泛型限定符}。
 * 默认情况下，所有侦听器都在调用线程中被调用。这会导致当前监听器阻塞整个应用程序，但增加的开销最小。
 *      我们也可以指定当前监听器在线程池中执行。{@link #setTaskExecutor}方法中指定线程池。
 */
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

    /**
     * 执行监听器的线程池，默认为 {@code null}，为空，则在调用线程中被调用，指定线程池则在线程池中被调用。
     */
    @Nullable
    private Executor taskExecutor;

    /**
     * 错误执行器。
     */
    @Nullable
    private ErrorHandler errorHandler;


    /**
     * 构造器
     */
    public SimpleApplicationEventMulticaster() {
    }

    /**
     * 构造器。
     */
    public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
        setBeanFactory(beanFactory);
    }


    /**
     * 设置一个自定义的线程池去处理所有的监听器。
     * 如果为null，那么监听器就会在被调用线程中执行，会阻塞当前线程。
     * @see org.springframework.core.task.SyncTaskExecutor：一个同步的线程池
     * @see org.springframework.core.task.SimpleAsyncTaskExecutor：一个异步的线程池
     */
    public void setTaskExecutor(@Nullable Executor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }

    /**
     * 返回线程池。
     */
    @Nullable
    protected Executor getTaskExecutor() {
        return this.taskExecutor;
    }

    /**
     * 设置异常处理策略，在监听器出现异常时的调用逻辑。
     */
    public void setErrorHandler(@Nullable ErrorHandler errorHandler) {
        this.errorHandler = errorHandler;
    }

    /**
     * 返回异常处理策略。
     */
    @Nullable
    protected ErrorHandler getErrorHandler() {
        return this.errorHandler;
    }


    /**
     * 发布事件。
     */
    @Override
    public void multicastEvent(ApplicationEvent event) {
        multicastEvent(event, resolveDefaultEventType(event));
    }

    /**
     * 广播一个事件的具体方法。
     */
    @Override
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        //1.获取监听器的线程池。
        Executor executor = getTaskExecutor();
        //2.拿到注册到多播器中的监听器。
        for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            if (executor != null) {
                //2.1：如果线程池不为空，调用线程池完成事件的处理，
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
                //2.2：在当前线程中完成事件的处理。
                invokeListener(listener, event);
            }
        }
    }

    private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
        return ResolvableType.forInstance(event);
    }

    /**
     * 调用监听器的方法去处理事件。
     */
    protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
        ErrorHandler errorHandler = getErrorHandler();
        if (errorHandler != null) {
            try {
                doInvokeListener(listener, event);
            }
            catch (Throwable err) {
                errorHandler.handleError(err);
            }
        }
        else {
            doInvokeListener(listener, event);
        }
    }

    /**
     * 实际上的处理方法。
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            //实际上的处理方法。
            listener.onApplicationEvent(event);
        }
        catch (ClassCastException ex) {
            String msg = ex.getMessage();
            if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
                // Possibly a lambda-defined listener which we could not resolve the generic event type for
                // -> let's suppress the exception and just log a debug message.
                Log logger = LogFactory.getLog(getClass());
                if (logger.isTraceEnabled()) {
                    logger.trace("Non-matching event type for listener: " + listener, ex);
                }
            }
            else {
                throw ex;
            }
        }
    }

    private boolean matchesClassCastMessage(String classCastMessage, Class<?> eventClass) {
        // On Java 8, the message starts with the class name: "java.lang.String cannot be cast..."
        if (classCastMessage.startsWith(eventClass.getName())) {
            return true;
        }
        // On Java 11, the message starts with "class ..." a.k.a. Class.toString()
        if (classCastMessage.startsWith(eventClass.toString())) {
            return true;
        }
        // On Java 9, the message used to contain the module name: "java.base/java.lang.String cannot be cast..."
        int moduleSeparatorIndex = classCastMessage.indexOf('/');
        if (moduleSeparatorIndex != -1 && classCastMessage.startsWith(eventClass.getName(), moduleSeparatorIndex + 1)) {
            return true;
        }
        // Assuming an unrelated class cast failure...
        return false;
    }

}
